// Learn TypeScript:
//  - https://docs.cocos.com/creator/manual/en/scripting/typescript.html
// Learn Attribute:
//  - https://docs.cocos.com/creator/manual/en/scripting/reference/attributes.html
// Learn life-cycle callbacks:
//  - https://docs.cocos.com/creator/manual/en/scripting/life-cycle-callbacks.html

import { menu } from "../../decorator";

const { ccclass, property } = cc._decorator;

@ccclass
@menu(__filename)
export default class touchMap extends cc.Component {
    @property
    _target: cc.Node = null
    @property(cc.Node)
    get target() { return this._target || this.node; }
    set target(value) {
        this._target = value;
    }

    @property({
        type: cc.Node,
        tooltip: '地图节点'
    })
    map: cc.Node = null;

    @property
    isMove: boolean = true;

    @property({
        visible() {
            return this.isMove;
        }
    })
    inertia: boolean = true;

    @property({
        visible() {
            return this.isMove;
        }
    })
    inertiaTime: number = 0.5;

    @property
    isScale: boolean = true;

    @property({
        visible() {
            return this.isScale;
        }
    })
    scaleMax: number = 2;

    @property({
        visible() {
            return this.isScale;
        }
    })
    scaleSpeed: number = 0.5;

    //是否缩放过
    private _has_scaled: boolean = false;
    private _last_timestamp: number = 0;
    private _inertia_dt: number = 0;
    private _start_timestamp: number = 0;

    _cancelEvent(target, event) {
        let cancelEvent = new cc.Event.EventTouch(event.getTouches(), event.bubbles);
        cancelEvent.type = cc.Node.EventType.TOUCH_CANCEL;
        cancelEvent.touch = event.touch;
        cancelEvent['simulate'] = true;
        target.dispatchEvent(cancelEvent);
    }

    onLoad() {
        const node = this.target;

        //注册缩放、移动相关事件
        var _touch_take = false;
        var _curr_target = null;
        node.on('touchstart', function (event) {
            _curr_target && this._cancelEvent(_curr_target, event);
            _curr_target = event.target;
            _touch_take = false;
        }, this, true);
        node.on('touchend', function (event) { (event.target === _curr_target) && (_curr_target = null); }, this, true);
        node.on('touchcancel', function (event) { (event.target === _curr_target) && (_curr_target = null); }, this, true);
        node.on('touchmove', function (event) {
            var touch = event.touch;

            if (!_touch_take && _curr_target !== node) {
                var deltaMove = touch.getLocation().sub(touch.getStartLocation());
                if (deltaMove.mag() > 7) {
                    this._cancelEvent(_curr_target, event);
                    _touch_take = true;
                }
            }

            var touches = event.getTouches(); //仅返回同时在动的触点,不动的触点不在数组内
            if (this.isMove && touches.length <= 1) {
                var touchDelta = event.getDelta();
                var newPos = this.map.position.add(touchDelta);
                //移动位置
                this.map.setPosition(this.fixPosition(newPos));
                this._last_timestamp = Date.now(); //最后一次移动时间戳
            }
            if (this.isScale && touches.length >= 2) {
                var scale = this.getScale(touches[0], touches[1]);
                var newPos = this.getAnchor(touches[0], touches[1], scale);
                //缩放并修正位置
                this.map.scale = scale;
                this.map.setPosition(this.fixPosition(newPos));
                this._has_scaled = true;
            }
        }, this, true);

        //注册惯性移动相关事件
        if (this.inertia) {
            node.on('touchstart', function (event) {
                this._start_timestamp = Date.now();
                this.unscheduleAllCallbacks(); //停止惯性移动
                this._has_scaled = false;
            }, this, true);
            node.on('touchend', function (event) {
                this.isMove && this.inertiaMove(event);
            }, this, true);
            node.on('touchcancel', function (event) {
                this.isMove && this.inertiaMove(event);
            }, this, true);
        }
    }

    //惯性移动
    inertiaMove(event) {
        if (this._has_scaled || Date.now() - this._last_timestamp > 30) {
            return;
        }
        this._inertia_dt = 0;
        //var d_vector = cc.pSub(event.getLocation(), event.getStartLocation());
        var d_vector = event.getLocation().sub(event.getStartLocation());
        var d_time = (Date.now() - this._start_timestamp) / 10;
        //d_vector = cc.pMult(d_vector, 1 / d_time); //0.01秒的平均移动向量
        d_vector = d_vector.mul(1 / d_time); //0.01秒的平均移动向量
        //实现移动
        this.schedule(function () {
            var newPos = this.fixPosition(this.map.position.add(d_vector.mul(1 - this._inertia_dt / this.inertiaTime)));

            this._inertia_dt += 0.01;
            if (this._inertia_dt >= this.inertiaTime || (this.map.x == newPos.x && this.map.y == newPos.y)) {
                this.unscheduleAllCallbacks(); //停止惯性移动
            } else {
                this.map.setPosition(newPos);
            }
        }, 0.01);
    }

    //得到移动范围
    getMoveRect() {
        var tBox = this.map.getBoundingBox();
        var nBox = (this.target || this.node).getBoundingBox();
        var w = tBox.width - nBox.width > 0 ? tBox.width - nBox.width : 0;
        var h = tBox.height - nBox.height > 0 ? tBox.height - nBox.height : 0;
        return cc.rect(-w / 2, -h / 2, w, h);
    }

    //修正坐标至移动范围内
    fixPosition(p) {
        var moveRect = this.getMoveRect();
        p.x = p.x < moveRect.xMin ? moveRect.xMin : p.x;
        p.x = p.x > moveRect.xMax ? moveRect.xMax : p.x;
        p.y = p.y < moveRect.yMin ? moveRect.yMin : p.y;
        p.y = p.y > moveRect.yMax ? moveRect.yMax : p.y;
        return p;
    }

    //得到缩放比例
    getScale(touch1, touch2) {
        const node = this.target || this.node;
        //var oldDistance = cc.pDistance(touch1.getPreviousLocation(), touch2.getPreviousLocation());
        //var newDistance = cc.pDistance(touch1.getLocation(), touch2.getLocation());
        var oldDistance = touch1.getPreviousLocation().sub(touch2.getPreviousLocation()).mag();
        var newDistance = touch1.getLocation().sub(touch2.getLocation()).mag();

        var scale = (newDistance - oldDistance) / oldDistance;
        scale = (1 + scale * this.scaleSpeed) * this.map.scale;

        //限制缩放大小
        if (scale > this.scaleMax) {
            scale = this.scaleMax;
        }
        if (this.map.width * scale < node.width) {
            scale = node.width / this.map.width;
        }
        if (this.map.height * scale < node.height) {
            scale = node.height / this.map.height;
        }
        return scale;
    }

    //得到缩放后锚点位置(实现缩放中心点位置不变)
    getAnchor(touch1, touch2, scale) {
        //var touchCenter = cc.pMidpoint(touch1.getPreviousLocation(), touch2.getPreviousLocation());
        var touchCenter = touch1.getPreviousLocation().add(touch2.getPreviousLocation()).mul(0.5);

        var pointCenter = this.map.convertToNodeSpaceAR(touchCenter);
        //var oldPoint = cc.pMult(pointCenter, this.map.scale);
        var oldPoint = pointCenter.mul(this.map.scale);
        //var newPoint = cc.pMult(pointCenter, scale);
        var newPoint = pointCenter.mul(scale);
        //var delta = cc.pSub(newPoint, oldPoint);
        var delta = newPoint.sub(oldPoint);
        //var newPos = cc.pSub(this.map.getPosition(), delta);
        var newPos = this.map.getPosition().sub(delta);
        return newPos;
    }

    //滚动地图
    scrollTo(time, pos, onEnd) {
        this.unscheduleAllCallbacks();
        this.map.stopAllActions();
        var a1 = cc.moveTo(time, pos.x, pos.y);
        var a2 = cc.callFunc(function () {
            typeof (onEnd) == 'function' && onEnd();
        }, this);
        var action = cc.sequence(a1, a2);
        this.map.runAction(action);
    }

    //滚动地图到最左边
    scrollToLeft(time, onEnd) {
        var rect = this.getMoveRect();
        this.scrollTo(time, cc.v2(rect.x + rect.width, this.map.y), onEnd);
    }

    //滚动地图到最右边
    scrollToRight(time, onEnd) {
        var rect = this.getMoveRect();
        this.scrollTo(time, cc.v2(rect.x, this.map.y), onEnd);
    }

    //滚动地图到最上边
    scrollToTop(time, onEnd) {
        var rect = this.getMoveRect();
        this.scrollTo(time, cc.v2(this.map.x, rect.y), onEnd);
    }

    //滚动地图到最下边
    scrollToBottom(time, onEnd) {
        var rect = this.getMoveRect();
        this.scrollTo(time, cc.v2(this.map.x, rect.y + rect.height), onEnd);
    }
}
